#include <set>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <sstream>
#include <algorithm>
#include <ncurses.h>
#include "ServerConnector.h"
#include "../Logger/Logger.h"
const int ServerConnector::BUFFER_LENGTH = 1024;



int ServerConnector::sendMessageToClient(const int client, const std::string &message)
{
  bzero(buffer,BUFFER_LENGTH);
  strcpy(buffer, message.c_str());
  log("message to send from server:");
  log(buffer);
  //int n = write(client,buffer,strlen(buffer));
  send(client, buffer, strlen(buffer), 0);
  /*if (n < 0) 
  {
     log("ERROR writing to socket", CRITICAL);
  }*/
  return 0;
}

ServerConnector::ServerConnector():settings_(Settings::getInstance()), lastConnected(-1), opened(false)
{
  portNum = 51717;
}

ServerConnector::~ServerConnector()
{
  close();
}

int ServerConnector::open()
{
  listener = socket(AF_INET, SOCK_STREAM, 0);
  if (listener < 0) 
  {
    log("ERROR opening socket", CRITICAL);
    return 1;
  }
  
  fcntl(listener, F_SETFL, O_NONBLOCK);
  bzero((char *) &serv_addr, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  
  //IP address of the host. INADDR_ANY - IP address of the local machine
  serv_addr.sin_addr.s_addr = INADDR_ANY;
  
  //htons converts port number in host byte order to a port number in network byte order
   serv_addr.sin_port = htons(portNum);
  
  if (bind(listener, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) 
  {
    log("ERROR on binding", CRITICAL);
    return 2;
  }
  opened = true;
  listen(listener, 5);
  clients.clear();

  FD_ZERO(&readset);
  FD_SET(listener, &readset);
  //log("SOCKET OPENED");
}

int ServerConnector::openCard(int playerNum)
{
  cmaker.setMaker(OPEN_CARD, playerNum, "");
  log("OPENING CARD");
  for (std::set<int>::iterator it = clients.begin(); it != clients.end(); ++it)
  { 
    log("Game message to send");
    log(cmaker.makeCommand());
    sendMessageToClient(*it, cmaker.makeCommand());
  }
  Connector::openCard(playerNum);
  return 0;  
}

int ServerConnector::getTotem(int playerNum)
{
  cmaker.setMaker(GET_TOTEM, playerNum, "");
  for (std::set<int>::iterator it = clients.begin(); it != clients.end(); ++it)
  { 
    sendMessageToClient(*it, cmaker.makeCommand());
  }
  Connector::getTotem(playerNum);
  return 0;  
}

int ServerConnector::connect(const std::string &name, const time_t &connectTime, int &error)
{
  int n = model_->getPlayersNumber();
  if(lastConnected > 0)
  {
    cmaker.setCommand(CONNECT);
    log("COMMAND SET");
    for (int i = 1; i <= n; ++i)
    {
      cmaker.setPlayerNumber(i);
      log("PLAYERNUM SET");
      cmaker.setPlayerName(model_->getPlayerName(i));
      log("PLAYERNAME SET");
      //sendMessageToClient(lastConnected, settings_.get("CONNECT") + model_->getPlayerName(i));
      sendMessageToClient(lastConnected, cmaker.makeCommand());
    }
  }

  log("PLAYER TO CONNECT ON SERVER");
  log(name);
  int playerNum = Connector::connect(name, connectTime, error);
  if (error != 0)
  {
    return 0;
  }
  cmaker.setMaker(CONNECT, playerNum, name);
  std::string command = cmaker.makeCommand();
  for (std::set<int>::iterator it = clients.begin(); it != clients.end(); ++it)
  { 
    log("sending message");
    if (*it != lastConnected)
    {
      sendMessageToClient(*it, command);
    }
  }
  
  if(lastConnected > 0)
  {
    cmaker.setCommand(CONNECT_CONFIRM);
    sendMessageToClient(lastConnected, cmaker.makeCommand());
  }
  registeredClients[lastConnected] = true;
  return 1;
}

bool ServerConnector::addClient(int newClient)
{
  //FD_SET(newClient, &readset);
  return clients.insert(newClient).second;
}

int ServerConnector::getMessage()
{

  parser.setBuffer(buffer);
  int parseError = parser.parseBuffer();
  
  Command command = parser.getCommand();

  if(parseError == 1)
  {
    log("PARSE ERROR!");
  }
  else
  {
    log("SERVER_PARSE_COMPLETED");
  }
  
  if (command == CONNECT)
  {
    log("SERVER_PARSER_NAME IS");
    log(parser.getPlayerName());
    int error = 0;
    std::string name = parser.getPlayerName();
    connect(name, time(0), error);
  }
  else if (command == GET_TOTEM)
  {
    getTotem(parser.getPlayerNumber());
  }
  else if (command == OPEN_CARD)
  {
    openCard(parser.getPlayerNumber());
  }
  else if (command == QUIT)
  {
    quit(parser.getPlayerNumber());
    clear();
    refresh();
    move(0,0);
    printw("%s leaved the game. Game over", model_->getPlayerName(parser.getPlayerNumber()).c_str());
    refresh();
    getch();
    return 1;
  }
  
  return 0;
}

int ServerConnector::acceptClients()
{
  FD_ZERO(&readset);
  FD_SET(listener, &readset);
  
  for(std::set<int>::iterator it = clients.begin(); it != clients.end(); it++)
  {
    FD_SET(*it, &readset);
  }
  
  FD_SET(STDIN_FILENO, &readset);

  
  // Задаём таймаут
  timeval timeout;
  timeout.tv_sec = 15;
  timeout.tv_usec = 0;

  // Ждём события в одном из сокетов
  int mx = std::max(listener, *max_element(clients.begin(), clients.end()));
  mx = std::max(mx, STDIN_FILENO);

  if(select(mx + 1, &readset, NULL, NULL, &timeout) <= 0)
  {
    //log("selectError");
    return 0;
  }
  
  // Определяем тип события и выполняем соответствующие действия
  if(FD_ISSET(listener, &readset))
  {
     //log("try to accept");
      //Поступил новый запрос на соединение, используем accept
     int sock = accept(listener, NULL, NULL);
     if(sock < 0)
     {
       //log("acceptError");
       return 0;
     }
     
     fcntl(sock, F_SETFL, O_NONBLOCK);
     clients.insert(sock);
     registeredClients[sock] = false;
     lastConnected = sock;
  }
  
  for(std::set<int>::iterator it = clients.begin(); it != clients.end(); it++)
  {
    if(FD_ISSET(*it, &readset))
    {
      // Поступили данные от клиента, читаем их
      bzero(buffer, BUFFER_LENGTH);
      //int n = read(*it, buffer, BUFFER_LENGTH);
      int n = recv(*it, buffer, BUFFER_LENGTH, 0);
      lastConnected = *it;
      if(n <= 0)
      {
        // Соединение разорвано, удаляем сокет из множества
        ::close(*it);
        clients.erase(*it);
        quit(0);
        return 1;
      }
      log(buffer);
      getMessage();
      // Отправляем данные обратно клиент
      //send(*it, buf, bytes_read, 0);
    }
  }
  
  if(FD_ISSET(STDIN_FILENO, &readset))
  {
    char c = getch();

    std::stringstream s;
    s << c;
    if (s.str() == settings_.get("START_KEY"))
    {
      int move = Connector::startGame(true);
      std::string command = cmaker.makeStartCommand(model_->getCards());
      for (std::set<int>::iterator it = clients.begin(); it != clients.end(); ++it)
      {
        sendMessageToClient(*it, command);
      }
 
      
      ::close(listener);
  
      //if player didn't send name, he is disconnected
      for (std::map<int, bool>::iterator it = registeredClients.begin(); it != registeredClients.end(); ++it)
      {
        if (!(it->second))
        {
          ::close(it->first);
          clients.erase(it->first);
        }
      } 
      return 1;
    }
  }
  return 0;
}


char ServerConnector::getMove(bool &toExit)
{
  FD_ZERO(&readset);
  
  for(std::set<int>::iterator it = clients.begin(); it != clients.end(); it++)
  {
    FD_SET(*it, &readset);
  }
  
  FD_SET(STDIN_FILENO, &readset);

  
  // Задаём таймаут
  timeval timeout;
  timeout.tv_sec = 15;
  timeout.tv_usec = 0;

  // Ждём события в одном из сокетов
  int mx = std::max(STDIN_FILENO, *max_element(clients.begin(), clients.end()));

  if(select(mx + 1, &readset, NULL, NULL, &timeout) <= 0)
  {
    //log("selectError");
    return 0;
  }
  
    
  for(std::set<int>::iterator it = clients.begin(); it != clients.end(); it++)
  {
    if(FD_ISSET(*it, &readset))
    {
      
      bzero(buffer, BUFFER_LENGTH);
      
      int n = recv(*it, buffer, BUFFER_LENGTH, 0);
      if (n <= 0)
      {
        quit(0);
        toExit = true;
        return 0;
      }
      log(buffer);
      toExit = getMessage();
      
    }
  }
  
  if(FD_ISSET(STDIN_FILENO, &readset))
  {
    char c = getch();
    return c;
  }
  return 0;
}


void ServerConnector::quit(int playerNum)
{
   cmaker.setMaker(QUIT, playerNum, "");
   std::string command = cmaker.makeCommand();
   for (std::set<int>::iterator it = clients.begin(); it != clients.end(); ++it)
   {
     sendMessageToClient(*it, command);
   }
  
   close();
   
}

void ServerConnector::close()
{
  if (opened)
   {
     for (std::set<int>::iterator it = clients.begin(); it != clients.end(); ++it)
     { 
       ::close(*it);
     }
     clients.clear();
   }
   
   
   opened = false;
}

void ServerConnector::disconnect()
{
  close();
}
